﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using QueueUnlimited.ScenarioTests.Helper;
using Microsoft.Data.Schema.UnitTesting;
using System.Threading;
using System.Diagnostics;
using System.Data.SqlClient;

namespace QueueUnlimited.ScenarioTests.API_Tests
{
    [TestClass]
    public class MessageListenerTests : BaseScenarioTestFixture
    {
        [TestMethod]
        public void Test_Listener_receives_one_message()
        {
            var eventReceived = new AutoResetEvent(false);
            var subscriber = new API.Subscriber("MyNewSubscriber");
            subscriber.AddEvent("MyFirstEvent");

            using (var connection = DatabaseTestClass.TestService.OpenExecutionContext().Connection as SqlConnection)
            using (var dataStore = new API.DataStore(connection))
            {
                dataStore.Configuration.Apply(subscriber);

                dataStore.Enqueue("MyFirstEvent", "My first message");
            }

            API.GenericEvent receivedEvent = null;

            using (var listener = new API.MessageListener((message) =>
            {
                receivedEvent = message;
                eventReceived.Set();
            }, ConnectionStringForTests, subscriber))
            {
                Thread.Sleep(15 * 1000); // Let's say the application is running for 15 seconds
            }

            eventReceived.WaitOne(new TimeSpan(0, 0, 25));

            Assert.IsNotNull(receivedEvent);
        }

        [TestMethod]
        public void Test_Listener_receives_message_transactional()
        {
            var eventReceived = new AutoResetEvent(false);
            var subscriber = new API.Subscriber("MyNewSubscriber");
            subscriber.AddEvent("MyFirstEvent");

            using (var connection = DatabaseTestClass.TestService.OpenExecutionContext().Connection as SqlConnection)
            using (var dataStore = new API.DataStore(connection))
            {
                dataStore.Configuration.Apply(subscriber);

                dataStore.Enqueue("MyFirstEvent", "My first message");
            }

            int numberOfCalls = 0;
            API.MessageProcessingFaultEventArgs faultArgs = null;
            using (var listener = new API.MessageListener((message) =>
            {
                Thread.Sleep(1 * 1000); // Let the OnMessageProcessingFault be attached first!
                numberOfCalls++;
                if (numberOfCalls == 1)
                    throw new ApplicationException("Oooh no I can't process the message the first time!");
                else
                    eventReceived.Set();
            }, ConnectionStringForTests, subscriber))
            {
                listener.OnMessageProcessingFault += (sender, args) => faultArgs = args;
                Thread.Sleep(15 * 1000); // Let's say the application is running for 15 seconds
            }

            eventReceived.WaitOne(new TimeSpan(0, 0, 25)); // Maybe the processing takes even longer, this is no performance test so we give the system some slack!

            Assert.AreEqual(2, numberOfCalls); // Second time, good time!
            Assert.IsNotNull(faultArgs);
            Assert.IsInstanceOfType(faultArgs.Exception, typeof(ApplicationException));
        }

        [TestMethod]
        public void Test_Listener_receives_five_messages()
        {
            var allEventsReceived = new AutoResetEvent(false);
            var subscriber = new API.Subscriber("MyNewSubscriber");
            subscriber.AddEvent("MyFirstEvent");

            using (var connection = DatabaseTestClass.TestService.OpenExecutionContext().Connection as SqlConnection)
            using (var dataStore = new API.DataStore(connection))
            {
                dataStore.Configuration.Apply(subscriber);
                for (int i = 0; i < 5; i++)
                    dataStore.Enqueue("MyFirstEvent", "My " + i + " message");
            }

            var receivedEvents = new List<API.GenericEvent>();

            using (var listener = new API.MessageListener((message) =>
            {
                receivedEvents.Add(message);
                if (receivedEvents.Count == 5)
                    allEventsReceived.Set();
            }, ConnectionStringForTests, subscriber))
            {
                Thread.Sleep(15 * 1000); // Let's say the application is running for 15 seconds
            }

            allEventsReceived.WaitOne(new TimeSpan(0, 0, 5));
            Assert.AreEqual(5, receivedEvents.Count);
            for (int i = 0; i < 5; i++) // Assert ordering
            {
                receivedEvents[i].Content.Contains(i.ToString());
            }
        }
    }
}
